package musicalgo;

import data.DataNormalizer;
import data.NormalizationStyle;
import data.SequentialData;
import musicalgo.data.DataIO;

import java.util.ArrayList;
import java.util.List;

/**
 * Base class for all algorithm variations that attempt to
 * solve the final project for Comp 424 Winter 2012
 * <p/>
 * provides methods for loading and saving the song data from the
 * provided files. assumes they are located in the same directory
 * that the application is being run from.
 */
public abstract class RecognitionAlgorithm
{

    //desired songs to use as total set (both training and validation)
    //this is for quickly determining the effect of tweaking several parameters
    int desiredSongCount = 2500;

    //between 1 and 200
    int desiredSegmentCount = 1;


    /**
     * percentage of overlap wrt to default size when merging song data
     */
    double percentOverlap = 0.1;


    /**
     * for feature selection
     */
    boolean[] includedFeatures;

    //training data
    protected ArrayList<SequentialData> trainingData = null;
    protected List<SequentialData> rawTrainingData = null;
    protected List<double[]> outputs = null;

    //testing data
    protected ArrayList<SequentialData> testData = null;
    protected List<SequentialData> rawTestData = null;

    protected NormalizationStyle normalizationStyle = NormalizationStyle.None;


    public void run()
    {
        loadTrainingData();
        train();
        loadTestingData();
        test();
    }


    protected abstract void train();

    protected abstract void test();


    private ArrayList<SequentialData> loadNormalized(String path)
    {
        ArrayList<SequentialData> data = DataIO.loadSongs(path);
        DataNormalizer normalizer = new DataNormalizer();
        normalizer.setRawData(data);
        normalizer.normalize(normalizationStyle);
        return data;

    }

    protected void loadTrainingData()
    {
        if (trainingData == null)
        {
            rawTrainingData = loadNormalized("trainx.txt").subList(0, Math.min(2500, desiredSongCount));

            System.out.println("Loaded " + rawTrainingData.size() + " Training Songs");

            trainingData = new ArrayList<SequentialData>(rawTrainingData.size());
            for (SequentialData s : rawTrainingData)
            {
                SequentialData sequentialData = includedFeatures == null ? s.getMergedData(desiredSegmentCount, percentOverlap) :
                        s.getMergedData(desiredSegmentCount, includedFeatures, percentOverlap);
                trainingData.add(sequentialData);
            }

            System.out.println("Training Data processed: segments per song: " + desiredSegmentCount + " Overlap Percentage:" + percentOverlap);


        }

        if (outputs == null)
            outputs = DataIO.loadDesiredOutputs("trainy.csv", 5).subList(0, Math.min(2500, desiredSongCount));
    }

    protected void loadTestingData()
    {
        if (testData == null)
        {
            rawTestData = loadNormalized("testx.txt");
            System.out.println("Loaded " + rawTestData.size() + " Testing Songs");

            testData = new ArrayList<SequentialData>(rawTestData.size());
            for (SequentialData s : rawTestData)
            {
                SequentialData sequentialData = includedFeatures == null ? s.getMergedData(desiredSegmentCount, percentOverlap) :
                        s.getMergedData(desiredSegmentCount, includedFeatures, percentOverlap);
                testData.add(sequentialData);
            }

            System.out.println("Testing Data processed: segments per song: " + desiredSegmentCount + " Overlap Percentage:" + percentOverlap);

        }
    }

    public NormalizationStyle getNormalizationStyle()
    {
        return normalizationStyle;
    }

    public void setNormalizationStyle(NormalizationStyle normalizationStyle)
    {
        this.normalizationStyle = normalizationStyle;
    }

    public void setDesiredSegmentCount(int desiredSegmentCount)
    {
        this.desiredSegmentCount = desiredSegmentCount;
    }

    public void setDesiredSongCount(int desiredSongCount)
    {
        this.desiredSongCount = desiredSongCount;
    }

    public void setIncludedFeatures(boolean[] includedFeatures)
    {
        this.includedFeatures = includedFeatures;
    }

    public void setPercentOverlap(double percentOverlap)
    {
        this.percentOverlap = percentOverlap;
    }
}
